package com.mall.product.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.mall.common.service.impl.CrudServiceImpl;
import com.mall.common.utils.ConvertUtils;
import com.mall.product.config.RedisConfig;
import com.mall.product.dao.CategoryDao;
import com.mall.product.dto.CategoryDTO;
import com.mall.product.entity.CategoryEntity;
import com.mall.product.service.CategoryBrandRelationService;
import com.mall.product.service.CategoryService;
import com.mall.product.vo.CatelogJsonVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.weaver.ast.Var;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 商品三级分类
 *
 * @author xjc xjc@163.com
 * @since 1.0.0 2022-07-17
 */
@Service
@Slf4j
public class CategoryServiceImpl extends CrudServiceImpl<CategoryDao, CategoryEntity, CategoryDTO> implements CategoryService {

    @Resource
    private CategoryBrandRelationService categoryBrandRelationService;
//    @Resource
//    private StringRedisTemplate redisTemplate;


    @Override
    public QueryWrapper<CategoryEntity> getWrapper(Map<String, Object> params) {
        String id = (String) params.get("id");

        QueryWrapper<CategoryEntity> wrapper = new QueryWrapper<>();
        wrapper.eq(StringUtils.isNotBlank(id), "id", id);

        return wrapper;
    }


    @Cacheable(value = "catalog",key = "#root.methodName",sync = true)
    @Override
    public List<CategoryDTO> treeList() {
        //
        List<CategoryEntity> allEntityList = baseDao.selectList(null);
        List<CategoryDTO> dtoList = ConvertUtils.sourceToTarget(allEntityList, CategoryDTO.class);

        return dtoList.stream().filter(categoryDTO -> categoryDTO.getParentCid() == 0)
                .peek(item -> item.setChildren(findChildren(item, dtoList)))
                .sorted()
                .collect(Collectors.toList());
    }

    @CacheEvict(value = "catalog", allEntries = true)
    @Override
    public void updateBatch(List<CategoryDTO> list) {
        List<CategoryEntity> entityList = ConvertUtils.sourceToTarget(list, CategoryEntity.class);
        boolean batchById = updateBatchById(entityList);
        if (batchById)
            log.info("批处理成功：跟新了" + list.size() + " 条。");
        else
            log.error("批处理失败。");
    }

    @Override
    public Long[] findPath(Long id) {
        List<Long> paths = new ArrayList<>();
        getPath(id, paths);
        return paths.toArray(new Long[paths.size()]);
    }

    @Override
    public Map<String, Object> findPathAndName(Long id) {
        Map<String, Object> res = new HashMap<>();
        List<Long> paths = new ArrayList<>();
        List<String> pathName = new ArrayList<>();
        getPathAndName(id, paths, pathName);
        res.put("pathId", paths);
        res.put("pathName", pathName);
        return res;
    }

    private void getPathAndName(Long id, List<Long> paths, List<String> pathName) {
        CategoryEntity category = baseDao.selectById(id);
        if (category != null) {
            if (category.getParentCid() != 0) {
                getPathAndName(category.getParentCid(), paths, pathName);
            }
            paths.add(category.getCatId());
            pathName.add(category.getName());
        }
    }

    @CacheEvict(value = "catalog",allEntries = true)
    @Transactional
    @Override
    public void updateCascade(CategoryDTO dto) {
        // 更新自己
        update(dto);
        // 级联更新
        categoryBrandRelationService.updateCategory(dto.getCatId(), dto.getName());
    }

    @Override
    public List<CategoryDTO> listParentCategory() {
        QueryWrapper<CategoryEntity> wrapper = new QueryWrapper<CategoryEntity>().eq("parent_cid", 0);
        List<CategoryEntity> entityList = baseDao.selectList(wrapper);
        return ConvertUtils.sourceToTarget(entityList, CategoryDTO.class);
    }


//    public Map<String, List<CatelogJsonVO>> getCatalogLevel_1() {
//        String key = "/index/json/catalog.json";
//        Map<String, List<CatelogJsonVO>> result = null;
//        ValueOperations<String, String> ops = redisTemplate.opsForValue();
//        // 先查询缓存
//        String catalogJson = ops.get(key);
//        if (StringUtils.isEmpty(catalogJson)) {
//            log.info("没有缓存：key = " + key);
//            while (StringUtils.isEmpty(ops.get(key))) {
//                // 创建一个锁id
//                String lock_key = "product-lock-catalog.json";
//                String lock_value = UUID.randomUUID().toString();
//                // 原则操作，保证分布式锁加锁正确
//                Boolean lock = ops.setIfAbsent(lock_key, lock_value, 30, TimeUnit.SECONDS);
//                if (Boolean.TRUE.equals(lock)) {
//                    log.info("加锁成功：thread = " + Thread.currentThread().getId());
//                    try {
//                        result = getCatalogLevelFromDB();
//                        ops.set(key, JSON.toJSONString(result));
//                    } catch (Exception e) {
//                        log.error(e.getMessage());
//                    } finally {
//                        // 解锁
//                        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
//                        Long execute = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList(lock_key), lock_value);
//                    }
//                    break;
//                } else {
//                    try {
//                        TimeUnit.SECONDS.sleep(200);
//                    } catch (InterruptedException e) {
//                        log.info(e.getMessage());
//                    }
//                }
//            }
//        } else {
//            log.info("缓存命中：key = " + key);
//            result = JSON.parseObject(catalogJson, new TypeReference<Map<String, List<CatelogJsonVO>>>() {
//            });
//        }
//        return result;
//    }

    @Cacheable(value = "catalog",key = "#root.methodName", sync = true)
    @Override
    public Map<String, List<CatelogJsonVO>> getCatalogLevel() {
        Map<String, List<CatelogJsonVO>> result = getCatalogLevelFromDB();
        return result;
    }

    @Override
    public List<CategoryEntity> listAll() {
        List<CategoryEntity> selectList = baseDao.selectList(new QueryWrapper<>());
        return selectList;
    }

    public Map<String, List<CatelogJsonVO>> getCatalogLevelFromDB() {
        // 一次查询
        List<CategoryEntity> entityList = baseDao.selectList(new QueryWrapper<CategoryEntity>());
        List<CategoryEntity> L1 = entityList.stream().filter(categoryEntity -> categoryEntity.getCatLevel() == 1).collect(Collectors.toList());
        List<CategoryEntity> L2 = entityList.stream().filter(categoryEntity -> categoryEntity.getCatLevel() == 2).collect(Collectors.toList());
        List<CategoryEntity> L3 = entityList.stream().filter(categoryEntity -> categoryEntity.getCatLevel() == 3).collect(Collectors.toList());

        // 第一层
        Map<String, List<CatelogJsonVO>> collect = L1.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), k -> {
            Long parentId = k.getCatId();
            // 第二层
            List<CatelogJsonVO> voList = L2.stream().filter(v2 -> Objects.equals(v2.getParentCid(), parentId)).map(v2 -> {
                CatelogJsonVO catelogJsonVO = new CatelogJsonVO();
                catelogJsonVO.setCatalog1Id(k.getCatId().toString());
                catelogJsonVO.setId(v2.getCatId().toString());
                catelogJsonVO.setName(v2.getName());
                // 第三层
                List<CatelogJsonVO.CatelogChild> children = L3.stream().filter(v3 -> Objects.equals(v3.getParentCid(), v2.getCatId())).map(v3 -> {
                    CatelogJsonVO.CatelogChild child = new CatelogJsonVO.CatelogChild();
                    child.setId(v3.getCatId().toString());
                    child.setCatalog2Id(v3.getCatId().toString());
                    child.setName(v3.getName());
                    return child;
                }).collect(Collectors.toList());
                catelogJsonVO.setCatalog3List(children);
                return catelogJsonVO;
            }).collect(Collectors.toList());
            return voList;
        }));
        return collect;
    }

    public void getPath(Long id, List<Long> paths) {
        CategoryEntity category = baseDao.selectById(id);
        if (category != null) {
            if (category.getParentCid() != 0)
                getPath(category.getParentCid(), paths);
            paths.add(category.getCatId());
        }
    }

    private List<CategoryDTO> findChildren(CategoryDTO root, List<CategoryDTO> allEntityList) {
        return allEntityList.stream().filter(categoryEntity -> categoryEntity.getParentCid().equals(root.getCatId()))
                .peek(item -> item.setChildren(findChildren(item, allEntityList)))
                .sorted()
                .collect(Collectors.toList());

    }
}